package com.sunday.redisson.lock;

import com.sunday.common.core.asserts.BusinessAssert;
import com.sunday.common.core.constants.StringConstants;
import com.sunday.common.core.lambda.LambdaOperation;
import com.sunday.redisson.annotation.OpenDistributedLock;
import com.sunday.redisson.core.LockFieldMapping;
import com.sunday.redisson.lambda.toolkit.support.LockColumnMapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.util.DigestUtils;

import java.util.Collection;
import java.util.function.Supplier;
import java.util.stream.Stream;

/**
 * 分布式ID效验器
 *
 * @author zsy
 * @since 2022/10/20
 */
@Slf4j
public class DistributedLockValidator {

    /**
     * 验证分布式锁，排他锁
     *
     * @param obj
     */
    public static void annotationLock(Object obj) {
        if (ObjectUtils.isEmpty(obj)) {
            return;
        }
        /** 解析类上的注解 **/
        if (obj.getClass().isAnnotationPresent(OpenDistributedLock.class)) {
            OpenDistributedLock lock = obj.getClass().getAnnotation(OpenDistributedLock.class);
            BusinessAssert.isTrue(
                    DistributedLock.lock(lock.value() + StringConstants.UNDERSCORE + DigestUtils.md5DigestAsHex(obj.toString().getBytes())),
                    lock.errorCodeEnum(),
                    message(lock.message(), lock.value()),
                    true);
        }
        /** 解析属性上的注解 **/
        LockFieldMapping.getColumnAdapter(obj.getClass()).forEach((fieldName, columnMapper) -> {
            if (columnMapper instanceof LockColumnMapper mapper) {
                Object fieldValue = LambdaOperation.getField(mapper.getField(), obj);
                if (ObjectUtils.isEmpty(fieldValue)) {
                    return;
                }
                if (fieldValue instanceof Collection<?> collection) {
                    collection.stream()
                            .filter(ObjectUtils::isNotEmpty)
                            .map(Object::toString)
                            .filter(StringUtils::isNotBlank)
                            .forEach(value ->
                                    BusinessAssert.isTrue(
                                            DistributedLock.lock(mapper.getColumnSelect() + value),
                                            mapper.getErrorCodeEnum(),
                                            message(mapper.getMessage(), mapper.getColumnSelect() + value),
                                            true)
                            );
                } else if (fieldValue.getClass().isArray()) {
                    Stream.of((Object[]) fieldValue).
                            filter(ObjectUtils::isNotEmpty)
                            .map(Object::toString)
                            .filter(StringUtils::isNotBlank)
                            .forEach(value ->
                                    BusinessAssert.isTrue(
                                            DistributedLock.lock(mapper.getColumnSelect() + value),
                                            mapper.getErrorCodeEnum(),
                                            message(mapper.getMessage(), mapper.getColumnSelect() + value),
                                            true)
                            );
                } else {
                    BusinessAssert.isTrue(
                            DistributedLock.lock(mapper.getColumnSelect() + fieldValue),
                            mapper.getErrorCodeEnum(),
                            message(mapper.getMessage(), mapper.getColumnSelect() + fieldValue),
                            true);
                }
            }
        });
    }

    private static Supplier<String> message(String errorMessage, String defaultMessage) {
        if (StringUtils.isNotBlank(errorMessage)) {
            return () -> errorMessage;
        }
        if (StringUtils.isNotBlank(defaultMessage)) {
            return () -> defaultMessage;
        }
        return null;
    }

}
